[日本語Alexa] Alexa SDK for Node.js Ver2入門(その1)はじめの一歩
1 はじめに
Alexaのスキル開発のためにNode.js用として Alexa Skills Kit for Node.js (以下、Alexa SDK) が提供されていますが、今月18日、大きく進化を遂げ、Version 2 となりました。
GitHub alexa/alexa-skills-kit-sdk-for-nodejs
こちらは、先月発表されたASK SDK v2 for Javaと同じコア機能セットとなっており、これにより、Amazonから提供されるスキル開発用のSDKは、JavaとNode.jsの2本だてとなりました。
本記事では、入門用という位置付けで、Alexa SDKの使い方を、順を追って紹介して行きたいと思います。(対象は、Node.js用のみです。Java用には触れておりません)
なお、以前に紹介した下記の記事は、初期バージョン(Ver1.x)のものであり、これらの焼き直しともなっています。
2 Alexa SDK とは
Alexaのスキル開発では、「データの永続性」「レスポンスの組み立て」「動作のモデリング」などに多大な労力が必要ですが、Alexa SDKを利用することで、その殆どを任せることができます。これにより開発者は、ロジックに集中することが可能になります。
Alexa SDKの特徴は、次のように列挙できます。
- NPMパッケージとして提供される
- コア機能のみのパッケージも利用可能 (Ver2)
- 各種のリクエストをハンドラとして定義できる
- ハンドラは、柔軟にグループ化や共通処理が可能(Ver2)
- セッションごとやセッションを跨ぐデータの永続化が可能 (Ver2)
- DynamoDB等のDBが使用可能(Ver2)
- すべての音声出力は、SSMLでラップされる
- 全てのLambdaイベントとコンテキストにアクセスが可能
- デバイスアドレスなどのAlexaサービスをラップし、簡単に利用可能 (Ver2)
- すべてのSDKツールが、handInputオブジェクトに用意され単体テストが可能 (Ver2)
- TypeScript定義ファイルと、.d.tsファイルの読み取りが可能 (Ver2)
- async/awaitが利用可能 (Ver2)
3 インストール
インストールは、npmコマンドで可能です。
$ npm install --save ask-sdk
node_modulesの下に、必要なモジュールが展開されますので、Lambdaへアップロードする際にzipに含まれるようにします。
ask-sdkは、依存関係を含めると概ね40Mbyteです。
$ du -k ./node_modules/ 38852 ./node_modules/
コア機能のみを利用する場合は、下記のように600KByte程度で利用することも可能です。
$ npm install --save ask-sdk-model $ npm install --save ask-sdk-core $ du -k ./node_modules/ 624 ./node_modules/
4 最初のサンプル
Alexa SDKを使用した最小限のスキルを書いてみました。
const Alexa = require('ask-sdk'); let skill; exports.handler = async function (event, context) { if (!skill) { skill = Alexa.SkillBuilders.custom() .addRequestHandlers(MyHandler) .create(); } return skill.invoke(event); } const MyHandler = { canHandle(handlerInput) { return true; }, handle(handlerInput) { return handlerInput.responseBuilder .speak('こんにちは') .getResponse(); } };
Alexa SDKでは、SkillBuildersでスキルオブジェクトを生成し、ハンドラ(MyHandler)を追加していきます。そして、Lambdaのパラメータ(event)を指定したinvoke()により、Lambdaの戻り値を返しています。
ここで追加したハンドラ(MyHandler)では、canHandle()でtrue(常に処理する)を返しているため、このハンドラが、すべての処理を担うことになります。
MyHandlerのhandle()では、responseBuilderを使用してレスポンスを組みたて、最後にgetResponse()でレスポンスを返します。
上記のサンプルは、何を話しかけても "こんにちは" と返ってくる(だけの)スキルです。
LaunchRequestリクエストで起動している状況
IntentRequest(HelloWorldIntent)リクエストで起動している状況
5 会話の継続と終了
アレクサからの発話には、会話を継続するものと、終了させるものの2種類があります。Alexa SDKのVer1.xでは、:tell と :ask で簡単に記述できるようになっていましたが、残念ながら、Ver2では提供されていません。
会話の継続と終了は、ResponseBuilderにreprompt()を追加するかどうかで決まります。
(1) 会話(セッション)を終了させる場合
ResponseBuilderにspeak()だけを追加すると、会話は終了します。
return handlerInput.responseBuilder .speak('こんにちは') .getResponse();
レスポンスには、shouldEndSessionキーが含まれていないため、デフォル値であるtrueとなり、アレクサの発話の後、直ちにセッションは終了します。
(2) 会話(セッション)を継続する場合
一方、会話を継続したい場合は、speak()にプラスしてreprompt()を追加します。
return handlerInput.responseBuilder .speak('何になさいますか') .reprompt('ご注文をお願いします') .getResponse();
こうすることで、レスポンスでは、shouldEndSessionキーがfalseとなり、会話は継続となります。
会話継続にした場合は、アレクサの発話の後、ユーザーからの返答を待つようになります。 もし、このタイミングでユーザーからの返事がない場合、今度は、催促するようにreprompt()で指定した内容が発話されます。(それでも、ユーザーからの返事がない場合は、セッションは終了します)
Alexa:「何になさいますか」 ユーザー:...... Alexa:「ご注文をお願いします」
6 複数のハンドラー定義
ハンドラーは、addRequestHandlersによって幾つでも追加することができます。
skill = Alexa.SkillBuilders.custom() .addRequestHandlers( Handler1, Handler2, Handler3) .create();
そして、どのリクエストをどのハンドラで処理するかは、canHandle()の戻り値で区別します。最初のサンプルでは、ここでtrueを返していたので、すべてのリクエストを処理するようになっていました。
const MyHandler = { canHandle(handlerInput) { return true; }, handle(handlerInput) { // ハンドラーの処理 } };
canHandle()で、対象となるリクエストを絞ることで、複数のハンドラを定義できます。以下の例では、LauchRequestとIntentRequestのOrderIntentを処理するハンドラを定義しています。
const LaunchRequestHandler = { canHandle(handlerInput) { return handlerInput.requestEnvelope.request.type === 'LaunchRequest'; }, handle(handlerInput) { // LaunchRequestの処理 } }; const OrderIntentHandler = { canHandle(handlerInput) { return handlerInput.requestEnvelope.request.type === 'IntentRequest' && handlerInput.requestEnvelope.request.intent.name === 'OrderIntent'; }, handle(handlerInput) { // OrderIntentの処理 } };
7 一連の流れを書いてみる
それでは、一連の流れを Alexa SDK で記述してみることにします。
サンプルは、簡単なコーヒーショップのスキルです。スキルを起動すると「ようこそ・・・」というようなウエルカムメッセージを返し、注文を聞きます。 コーヒーを注文をすると、スキルは終了します。
定義されているインテントは、以下のOrderIntentだけです。
呼び出し名は、「コーヒーショップ」になっています。
コードは、下記の通りです。
const Alexa = require('ask-sdk'); let skill; exports.handler = async function (event, context) { if (!skill) { skill = Alexa.SkillBuilders.custom() .addRequestHandlers(LaunchRequestHandler,OrderIntentHandler) .create(); } return skill.invoke(event); } const LaunchRequestHandler = { canHandle(handlerInput) { return handlerInput.requestEnvelope.request.type === 'LaunchRequest'; }, handle(handlerInput) { return handlerInput.responseBuilder .speak('ようこそ、クラスメソッド・コーヒーショップへ、ご注文をどうぞ') .reprompt('ご注文をお伺いします') .getResponse(); } }; const OrderIntentHandler = { canHandle(handlerInput) { return handlerInput.requestEnvelope.request.type === 'IntentRequest' && handlerInput.requestEnvelope.request.intent.name === 'OrderIntent'; }, handle(handlerInput) { return handlerInput.responseBuilder .speak('ご注文ありがとうございました') .getResponse(); } };
動作している様子です。
8 最後に
Ver1.xにあった、ステートごとのハンドラ定義、また、リソースを定義してthis.t(キー文字列) で使用するような手法は、標準機能からは無くなっています。(同等の記述は可能)
今回紹介したサンプルは、必須のハンドラであるAMAZON.HelpIntent、StopIntent、CancelIntentの処理や、エラー処理などは、まったく行なっておりません。これらについては次回以降、逐次紹介させて頂きたいと考えております。
9 参考リンク
Alexa Skills Kit SDK for Node.js
Now Available: Version 2 of the ASK Software Development Kit for Node.js
GitHub repository for the SDK v2 for Node.js
[日本語Alexa] Alexa SDK for Node.js Ver2入門(その1)はじめの一歩
[日本語Alexa] Alexa SDK for Node.js Ver2入門(その2)ハンドラの登録
[日本語Alexa] Alexa SDK for Node.js Ver2入門(その3)レスポンスの作成
[日本語Alexa] Alexa-SDK Ver2(その4)スキル属性
[日本語Alexa] Alexa-SDK Ver2(その5)ダイアログモード